package keqiang.com.kq_flutter_core_widget.util.wechat;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import com.keqiang.base.Logger;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.modelmsg.*;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import keqiang.com.kq_flutter_core_widget.util.ErrCode;

import java.io.File;
import java.util.ArrayList;
import java.util.Objects;
import java.util.UUID;

/**
 * 提供与微信操作Api
 *
 * @author Created by wanggaowan on 2024/12/18 下午3:52
 */
public class WXAPI {
    private static final String TAG = WXAPI.class.getSimpleName();
    private static WXAPI mWxApi;
    
    private final String mAppId;
    private final IWXAPI mApi;
    /**
     * 是否已经向微信注册过且注册成功
     */
    private boolean registerApp;
    private final WXApiEventHandlerImpl mEventHandler = new WXApiEventHandlerImpl();
    
    
    private WXAPI(@NonNull Context context, @NonNull String appId) {
        mAppId = appId;
        mApi = WXAPIFactory.createWXAPI(context, appId);
        registerApp();
    }
    
    @NonNull
    static WXAPI getApi(@NonNull Context context, @NonNull String appId) {
        if (mWxApi == null || !appId.equals(mWxApi.mAppId)) {
            mWxApi = new WXAPI(context, appId);
        }
        return mWxApi;
    }
    
    /**
     * 向微信注册App
     */
    private void registerApp() {
        if (registerApp) {
            return;
        }
        
        registerApp = mApi.registerApp(mAppId);
    }
    
    static void handIntent(@Nullable Intent intent) {
        WXAPI api = WXAPI.mWxApi;
        if (api == null) {
            return;
        }
        
        if (intent == null) {
            api.mEventHandler.onResp(null);
            return;
        }
        
        try {
            api.mApi.handleIntent(intent, api.mEventHandler);
        } catch (Exception e) {
            Logger.printStackTrace(e);
        }
    }
    
    private static void parseLoginResp(BaseResp baseResp, LoginCallback callback) {
        if (!(baseResp instanceof SendAuth.Resp)) {
            callback.callback(ErrCode.FAILED, null);
            Logger.e(TAG, "授权失败");
            return;
        }
        
        SendAuth.Resp resp = (SendAuth.Resp) baseResp;
        if (!Objects.equals(resp.state, resp.transaction)) {
            // 微信返回state与本地发送state不一致，可能被篡改
            callback.callback(ErrCode.FAILED, null);
            Logger.e(TAG, "授权不可靠");
        } else if (resp.errCode == BaseResp.ErrCode.ERR_USER_CANCEL) {
            callback.callback(ErrCode.CANCEL, null);
            Logger.e(TAG, "用户取消授权");
        } else if (resp.errCode != BaseResp.ErrCode.ERR_OK) {
            callback.callback(ErrCode.FAILED, null);
            Logger.e(TAG, "授权失败");
        } else {
            Logger.e(TAG, "授权成功");
            callback.callback(ErrCode.SUCCESS, resp.code);
        }
    }
    
    private static void parseShareResp(BaseResp baseResp, Callback callback) {
        if (!(baseResp instanceof SendMessageToWX.Resp)) {
            callback.callback(ErrCode.FAILED);
            Logger.e(TAG, "授权失败");
            return;
        }
        
        SendMessageToWX.Resp resp = (SendMessageToWX.Resp) baseResp;
        if (resp.errCode == BaseResp.ErrCode.ERR_USER_CANCEL) {
            callback.callback(ErrCode.CANCEL);
            Logger.e(TAG, "用户取消授权");
        } else if (resp.errCode != BaseResp.ErrCode.ERR_OK) {
            callback.callback(ErrCode.FAILED);
            Logger.e(TAG, "授权失败");
        } else {
            Logger.e(TAG, "授权成功");
            callback.callback(ErrCode.SUCCESS);
        }
    }
    
    public static void authLogin(@NonNull Context context, @NonNull String appId, @NonNull LoginCallback callback) {
        WXAPI api = WXAPI.getApi(context, appId);
        int code = checkApi(api);
        if (code != ErrCode.SUCCESS) {
            callback.callback(code, null);
            return;
        }
        
        String mToken = UUID.randomUUID().toString();
        SendAuth.Req req = new SendAuth.Req();
        // 作用域：获取用户信息
        req.scope = "snsapi_userinfo";
        req.transaction = mToken;
        req.state = mToken;
        
        boolean success = api.mApi.sendReq(req);
        if (success) {
            RespCallback respCallback = new RespCallback() {
                @Override
                public void callback(@Nullable BaseResp baseResp) {
                    api.mEventHandler.unregisterOnWXRespCallback(this);
                    parseLoginResp(baseResp, callback);
                }
            };
            api.mEventHandler.registerOnWXRespCallback(respCallback);
        }
    }
    
    private static int checkApi(WXAPI api) {
        if (!api.registerApp) {
            api.registerApp();
        }
        
        if (!api.registerApp) {
            Logger.e(TAG, "程序注册到微信失败");
            return ErrCode.FAILED;
        }
        
        if (!api.mApi.isWXAppInstalled()) {
            Logger.e(TAG, "app未安装");
            return ErrCode.APP_NOT_INSTALL;
        }
        return ErrCode.SUCCESS;
    }
    
    public static void shareTextData(@NonNull Context context, @NonNull String appId, @NonNull String content,
                                     String title, String desc, byte[] icon, Callback callback) {
        WXTextObject textObject = new WXTextObject();
        textObject.text = content;
        shareData(context, appId, textObject, title, desc, icon, callback);
    }
    
    public static void shareWebData(@NonNull Context context, @NonNull String appId, @NonNull String url,
                                    String title, String desc, byte[] icon, Callback callback) {
        WXWebpageObject webpage = new WXWebpageObject();
        webpage.webpageUrl = url;
        shareData(context, appId, webpage, title, desc, icon, callback);
    }
    
    public static void shareImageData(@NonNull Context context, @NonNull String appId, String localPath,
                                      byte[] bytes, Callback callback) {
        WXImageObject imageObject = new WXImageObject();
        if (bytes != null && bytes.length > 0) {
            imageObject.imageData = bytes;
        } else if (!TextUtils.isEmpty(localPath)) {
            imageObject.imagePath = WXAPI.getApi(context, appId).getShareFilePath(context, localPath);
        }
        shareData(context, appId, imageObject, null, null, null, callback);
    }
    
    private String getShareFilePath(Context context, String localPath) {
        if (mApi != null && mApi.getWXAppSupportAPI() >= 0x27000D00) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                try {
                    ProviderInfo providerInfo = null;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                        providerInfo = context.getPackageManager().getProviderInfo(new ComponentName(context, WXFileProvider.class), PackageManager.ComponentInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
                    } else {
                        providerInfo = context.getPackageManager().getProviderInfo(new ComponentName(context, WXFileProvider.class), PackageManager.MATCH_DEFAULT_ONLY);
                    }
                    final Uri shareFileUri = FileProvider.getUriForFile(context, providerInfo.authority, new File(localPath));
                    context.grantUriPermission("com.tencent.mm", shareFileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    return shareFileUri.toString();
                } catch (PackageManager.NameNotFoundException e) {
                    // ignore
                }
            }
        }
        return localPath;
    }
    
    public static void shareData(@NonNull Context context, @NonNull String appId, @NonNull WXMediaMessage.IMediaObject mediaObject,
                                 String title, String desc, byte[] icon, Callback callback) {
        WXAPI api = WXAPI.getApi(context, appId);
        int code = checkApi(api);
        if (code != ErrCode.SUCCESS) {
            callback.callback(code);
            return;
        }
        
        WXMediaMessage msg = new WXMediaMessage(mediaObject);
        msg.title = title;
        msg.description = desc;
        msg.thumbData = icon;
        
        String mToken = UUID.randomUUID().toString();
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.message = msg;
        req.scene = SendMessageToWX.Req.WXSceneSession;
        req.transaction = mToken;
        
        boolean success = api.mApi.sendReq(req);
        if (!success) {
            if (callback != null) {
                callback.callback(ErrCode.FAILED);
            }
            return;
        }
        
        if (callback != null) {
            RespCallback respCallback = new RespCallback() {
                @Override
                public void callback(@Nullable BaseResp baseResp) {
                    api.mEventHandler.unregisterOnWXRespCallback(this);
                    parseShareResp(baseResp, callback);
                }
            };
            api.mEventHandler.registerOnWXRespCallback(respCallback);
        }
    }
    
    public interface LoginCallback {
        /**
         * @param code     授权结果：{@link ErrCode}
         * @param authCode 授权码，失败时为null
         */
        void callback(int code, String authCode);
    }
    
    public interface Callback {
        /**
         * @param code 授权结果：{@link ErrCode}
         */
        void callback(int code);
    }
    
    private interface RespCallback {
        void callback(@Nullable BaseResp baseResp);
    }
    
    private static class WXApiEventHandlerImpl implements IWXAPIEventHandler {
        private final ArrayList<RespCallback> onRespCallbacks = new ArrayList<>();
        
        public void registerOnWXRespCallback(RespCallback on) {
            this.onRespCallbacks.add(on);
        }
        
        public void unregisterOnWXRespCallback(RespCallback on) {
            this.onRespCallbacks.remove(on);
        }
        
        @Override
        public void onReq(BaseReq baseReq) {
        
        }
        
        @Override
        public void onResp(BaseResp baseResp) {
            ArrayList<RespCallback> callbacks = new ArrayList<>(onRespCallbacks);
            for (RespCallback callback : callbacks) {
                callback.callback(baseResp);
            }
        }
    }
}

